names(list_of_cents)
[1] "fold_change_aritmetic" "fold_change_geometric" "fold_change_quadratic" "fold_change_harmonic"  "delta_aritmetic"       "delta_geometric"       "delta_quadratic"       "delta_harmonic"       

Density plot of centralities



plot_density <- function(df, titulo ){cut = 0.01
                             df %>% gather() -> df_gathered
                              df_gathered['value'] %>% filter(abs(value) > cut) %>%
                              ggplot( aes(x=value)) +
                                    geom_density(fill="deepskyblue4", color="azure4", alpha=.8)+
                                       theme(legend.position="top") +
                                    ylab("Density") + ggtitle(titulo) +
                                    xlab("Centrality") + geom_vline(xintercept=0, linetype="dashed", color = "red",  size=.4) -> Density_plot_of_centralities
                              return(Density_plot_of_centralities)}



map2(list_of_cents, names(list_of_cents), plot_density)
$fold_change_aritmetic

$fold_change_geometric

$fold_change_quadratic

$fold_change_harmonic

$delta_aritmetic

$delta_geometric

$delta_quadratic

$delta_harmonic

#centralities <-  Results[,!is.na(as.numeric(colnames(Results)))] %>% gather()

Density_plot_of_centralities.panel <- ggarrange( NULL,Density_plot_of_centralities, nrow = 2,  heights= c(1, .4), labels = c('a','b'),
                                                 hjust = -0.1, vjust = .8)

Density_plot_of_centralities.panel
fba_list_of_cents
$fold_change_aritmetic

$fold_change_geometric

$fold_change_quadratic

$fold_change_harmonic

$delta_aritmetic

$delta_geometric

$delta_quadratic

$delta_harmonic
NA

Hierarchical clustering

library(tidyverse)
library(scales)


assign_node_type <- function(df){
data <- df  %>% rownames_to_column('ID') %>% filter(!str_detect(ID, regex('AA_|AA2', ignore_case = F)))  %>%
         mutate(`Node type` = ifelse(str_detect(Reaction, regex('\\[[a-z]A\\]',              ignore_case = T)), 'Astrocyte', NA)) %>% 
         mutate(`Node type` = ifelse(str_detect(Reaction, regex('\\[[a-z]N\\]',              ignore_case = T)), 'Neuron', `Node type`))    %>% 
         mutate(`Node type` = ifelse( str_detect(Reaction, regex('\\[[a-z]A\\]|\\[[a-z]N\\]',ignore_case = T), negate = T), 'Exchange', `Node type`)) %>%
         mutate(`Node type` = ifelse(str_detect(Reaction, regex('\\[[a-z]A\\]',ignore_case = T)) & 
                                       str_detect(Name, regex('DM_|Demand|sink',ignore_case = T)), 'Sink/Demand Astro', `Node type`))%>%
         mutate(`Node type` = ifelse(str_detect(Reaction, regex('\\[[a-z]N\\]',ignore_case = T)) & 
                                       str_detect(Name, regex('DM_|Demand|sink',ignore_case = T)), 'Sink/Demand Neuron', `Node type`))
                                    return(data)}



map(fba_list_of_cents, assign_node_type)
Error: Problem with `mutate()` input `Node type`.
x objeto 'Name' no encontrado
ℹ Input `Node type` is `ifelse(...)`.
Run `rlang::last_error()` to see where the error occurred.
data %>% select(matches('^\\d+'))       -> centrality.matrix

centrality.matrix %>% abs %>% rowSums()  -> total.abs.centrality #vector de centralidades totales

pseudoLog10 <- function(x) { asinh(x/2)/log(10) }

data$Fluxes   %>% abs %>% as.matrix()  %>% pseudoLog10  %>% rescale(c(0,1))  -> fluxes
data$RedCosts  %>% abs %>% as.matrix()   %>% pseudoLog10  %>% rescale(c(0,1))-> sensitivities 

fluxes + sensitivities  -> summaryzed.optimality

heatmap


library(ComplexHeatmap)
scale_rows <- function(x){t(scale(t(x)))}

data %>% column_to_rownames('ID')   %>% select(matches("^\\d+")) %>% t %>% scale_rows  %>% cor(method = "kendall") -> my.corr.mat

left_annotation <- rowAnnotation(`Nodes` = data$`Node type`, col = list( `Nodes` = c(
                                    "Astrocyte"        = "deepskyblue3", 
                                    'Exchange'   = 'darkgreen',
                                    'Sink/Demand Astro'='darkgoldenrod',
                                    "Neuron"           = "brown2",
                                    'Sink/Demand Neuron'= 'blueviolet' )))
pseudoLog10 <- function(x) { asinh(x/2)/log(10) }

right_annotation <-   
  rowAnnotation(gap = unit(12, "points"),Ce      = anno_barplot(bar_width = 0.01,width = unit(1.5, "cm"), border = T,total.abs.centrality, gp = gpar(col = 'azure4')),
            Op        = anno_barplot(bar_width = 0.01,width = unit(1.5, "cm") ,border = T, summaryzed.optimality, gp = gpar(col = 'azure4')))


ht <- Heatmap(my.corr.mat, name = "Correlation",  left_annotation =left_annotation,
                                            right_annotation=right_annotation,
                                 clustering_distance_columns  = function(m)   dist(m, method = 'euclidean'),
                                 cluster_columns              = function(x) fastcluster::hclust(dist(x), "average"),
              
                                clustering_distance_rows   = function(m)   dist(m, method = 'euclidean'),
                                 cluster_rows               = function(x) fastcluster::hclust(dist(x), "average"),
                                row_km = 2,
                                column_km = 2,
                                # row_split = paste0("cluster ", pa$clustering),
                                # column_split = paste0("cluster ", pa$clustering),
                                 border = TRUE,
                                 row_dend_width    = unit(3, "cm"),
                                 row_gap = unit(2, "mm"),
                                 column_gap = unit(2, "mm"),
                                  width = unit(10, "cm"), 
                                height = unit(10, "cm"),
                                column_title = c("Astrocytic cluster", "Neuronal cluster"),
                                column_title_gp = gpar(fontsize = 10),
                                  row_title_rot  = 0,
                                 show_column_names    = F,
                                show_row_names    = F,
                                 row_names_gp = gpar(fontsize = 8),
                                  row_title = c("Astrocytic\n cluster", "Neuronal\n cluster"),
                             
                                 row_title_gp = gpar(fontsize = 10))
my_heatmap = grid.grabExpr(draw(ht))

ht

 upper_panel <- ggarrange( Density_plot_of_centralities.panel,  my_heatmap, ncol = 2, widths = c(.4, 1), heights = c(1,.3), labels = c('','c'),
                                                 hjust = -4, vjust = .8)

upper_panel

Distribution of pairwise correlations

row_order(ht)[[1]] -> astro_cluster
row_order(ht)[[2]] -> neuron_cluster
data$`Node type`   -> Nodes
data$ID[neuron_cluster]     -> neuron_cluster_names
data$ID[astro_cluster]      -> astro_cluster_names
ht@matrix %>% as.data.frame -> heatmap_matrix

heatmap_matrix[neuron_cluster_names,neuron_cluster_names] %>% as.matrix %>% c -> `Neuronal_cluster`
heatmap_matrix[astro_cluster_names,astro_cluster_names] %>% as.matrix %>% c  ->`Astrocytic_cluster` 
heatmap_matrix[astro_cluster_names,neuron_cluster_names] %>% as.matrix %>% c  -> `Neuron_vs_Astrocyte`

data.frame(`Neuronal_cluster`) %>% gather  -> A
data.frame(`Astrocytic_cluster`) %>% gather   -> B
data.frame( `Neuron_vs_Astrocyte`) %>% gather -> C
quads <- rbind(A,B,C)
colnames(quads) <- c("Comparison","Node correlation")

#quads$Comparison %>% unique()

my_comparisons <- list( c("Neuronal_cluster", "Astrocytic_cluster"), 
                        c("Neuronal_cluster", "Neuron_vs_Astrocyte"), 
                        c("Astrocytic_cluster", "Neuron_vs_Astrocyte") )
ggplot(quads, aes(x=Comparison, y=`Node correlation`, fill=Comparison)) + 
    geom_violin(trim=F, colour = "azure4")+  
    stat_compare_means( vjust= -1., hjust= 0, comparisons = my_comparisons, method = "wilcox.test", p.adjust.method = "bonferroni", label = "p.signif")+
   scale_fill_manual(values = alpha(c("deepskyblue3", "blueviolet", "brown2"), .8)) + 
   theme(plot.title = element_text(hjust = 0.5),legend.position="none",  axis.title.x=element_blank(),
         axis.text.y = element_text(angle = 45, hjust = 1), axis.text.x = element_text(angle = 50, hjust = 1))  + 
  #ggtitle("Neuronal self-correlations") + 
  scale_y_continuous(limits=c(-0.7, 2)) + geom_hline(yintercept=0, linetype="dashed", color = "darkred",  size=1)-> corr_comparisons

corr_comparisons

library(ProjectionBasedClustering)
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
library(PCAtools)
Loading required package: ggrepel

Attaching package: ‘PCAtools’

The following objects are masked from ‘package:stats’:

    biplot, screeplot
library(magrittr)

Attaching package: ‘magrittr’

The following object is masked from ‘package:purrr’:

    set_names

The following object is masked from ‘package:tidyr’:

    extract
p <- pca(my.corr.mat)

cbind(p$rotated$PC1, p$rotated$PC2) %>% as.matrix %>% as.data.frame-> my_pca




my_pca %>%  cbind( data$`Node type`) %>% set_colnames(c('PC1','PC2','node')) -> to.pca.scatter

summaryzed.optimality -> Op
total.abs.centrality -> Centrality
Centrality -> Ce

b <- ggplot(to.pca.scatter, aes(x = PC1, y = PC2)) 
b +   scale_color_manual(labels = c("Astro", "Exch",'Neu','Si/De Ast', 'Si/De Neu'), values =  c("deepskyblue3",'darkgreen','brown2','darkgoldenrod','blueviolet')) + 
  theme(legend.position="right", legend.box = "vertical")+ geom_point(aes(size = Op, color = node))-> pca_node_RedCosts




b <- ggplot(to.pca.scatter, aes(x = PC1, y = PC2)) 
b +   scale_color_manual(labels = c("Astro", "Exch",'Neu','Si/De Ast', 'Si/De Neu'), values =  c("deepskyblue3",'darkgreen','brown2','darkgoldenrod','blueviolet')) +
  theme(legend.position="right", legend.box = "vertical")+ geom_point(aes( size =  Ce,color = node))-> pca_node_centrality


bottom_panel <- ggarrange( corr_comparisons,pca_node_centrality, pca_node_RedCosts  , ncol = 3, widths  = c(.4,1,1) , labels = c('d','e','f'),
                                                 hjust = 0, vjust = 1)
Removed 3 rows containing non-finite values (stat_ydensity).Removed 3 rows containing non-finite values (stat_signif).Removed 26 rows containing missing values (geom_violin).

ggsave(file="panel_without_graph.png", plot=panel_without_graph, width=13, height=9, dpi = 320)

Node types for Networkx (python) attributes

library(tidyverse)
library(magrittr)

Results <- read_csv("Results.csv")

data_Networkx <- Results%>% 
         mutate(`Node_type` = ifelse(str_detect(Formula, regex('\\[[a-z]A\\]',              ignore_case = T)), 'Astrocyte', NA)) %>% 
         mutate(`Node_type` = ifelse(str_detect(Formula, regex('\\[[a-z]N\\]',              ignore_case = T)), 'Neuron', `Node_type`))    %>% 
         mutate(`Node_type` = ifelse( str_detect(Formula, regex('\\[[a-z]A\\]|\\[[a-z]N\\]',ignore_case = T), negate = T), 'Exchange', `Node_type`)) %>%
         mutate(`Node_type` = ifelse(str_detect(Formula, regex('\\[[a-z]A\\]',ignore_case = T)) & 
                                       str_detect(Name, regex('DM_|Demand|sink',ignore_case = T)), 'Sink/Demand Astro', `Node_type`))%>%
         mutate(`Node_type` = ifelse(str_detect(Formula, regex('\\[[a-z]N\\]',ignore_case = T)) & 
                                       str_detect(Name, regex('DM_|Demand|sink',ignore_case = T)), 'Sink/Demand Neuron', `Node_type`))


data_Networkx %<>% select(c(ID, Node_type))

write_csv(data_Networkx, 'data_Networkx.csv')
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KHJlYWR4bCkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShocmJydGhlbWVzKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KHZpcmlkaXMpCmxpYnJhcnkoZ2dwdWJyKQoKZGZsaXN0X25hbWVzID0gYygnZm9sZF9jaGFuZ2VfYXJpdG1ldGljJywgJ2ZvbGRfY2hhbmdlX2dlb21ldHJpYycsICdmb2xkX2NoYW5nZV9xdWFkcmF0aWMnLCAnZm9sZF9jaGFuZ2VfaGFybW9uaWMnLAogICAgJ2RlbHRhX2FyaXRtZXRpYycsICdkZWx0YV9nZW9tZXRyaWMnLCAnZGVsdGFfcXVhZHJhdGljJywgJ2RlbHRhX2hhcm1vbmljJykKCnJlYWRfc2hlZXRzIDwtIGZ1bmN0aW9uKGFfc2hlZXQpe2RmIDwtIHJlYWRfZXhjZWwoJ2RlbHRhX2ZjX2NlbnRyYWxpZGFkZXMueGxzeCcsIHNoZWV0ID0gYV9zaGVldCkgJT4lIGNvbHVtbl90b19yb3duYW1lcygiLi4uMSIpCiAgICAgICAgICAgICAgICAgIHJldHVybihkZil9CgptYXAoZGZsaXN0X25hbWVzLCByZWFkX3NoZWV0cykgJT4lIHB1cnJyOjpzZXRfbmFtZXMoZGZsaXN0X25hbWVzKSAtPiBsaXN0X29mX2NlbnRzCm5hbWVzKGxpc3Rfb2ZfY2VudHMpCmBgYAoKRGVuc2l0eSBwbG90IG9mIGNlbnRyYWxpdGllcwpgYGB7ciBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD00LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKCnBsb3RfZGVuc2l0eSA8LSBmdW5jdGlvbihkZiwgdGl0dWxvICl7Y3V0ID0gMC4wMQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRmICU+JSBnYXRoZXIoKSAtPiBkZl9nYXRoZXJlZAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZl9nYXRoZXJlZFsndmFsdWUnXSAlPiUgZmlsdGVyKGFicyh2YWx1ZSkgPiBjdXQpICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZ3Bsb3QoIGFlcyh4PXZhbHVlKSkgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW9tX2RlbnNpdHkoZmlsbD0iZGVlcHNreWJsdWU0IiwgY29sb3I9ImF6dXJlNCIsIGFscGhhPS44KSsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJ0b3AiKSArCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHlsYWIoIkRlbnNpdHkiKSArIGdndGl0bGUodGl0dWxvKSArCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhsYWIoIkNlbnRyYWxpdHkiKSArIGdlb21fdmxpbmUoeGludGVyY2VwdD0wLCBsaW5ldHlwZT0iZGFzaGVkIiwgY29sb3IgPSAicmVkIiwgIHNpemU9LjQpIC0+IERlbnNpdHlfcGxvdF9vZl9jZW50cmFsaXRpZXMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuKERlbnNpdHlfcGxvdF9vZl9jZW50cmFsaXRpZXMpfQoKCgptYXAyKGxpc3Rfb2ZfY2VudHMsIG5hbWVzKGxpc3Rfb2ZfY2VudHMpLCBwbG90X2RlbnNpdHkpCgojY2FsY3VsYXIgc3RkLCBrdXJ0b25pcyB5IHNrZXduZXNzLgpgYGAKCgpgYGB7ciBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD00LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojY2VudHJhbGl0aWVzIDwtICBSZXN1bHRzWywhaXMubmEoYXMubnVtZXJpYyhjb2xuYW1lcyhSZXN1bHRzKSkpXSAlPiUgZ2F0aGVyKCkKCkRlbnNpdHlfcGxvdF9vZl9jZW50cmFsaXRpZXMucGFuZWwgPC0gZ2dhcnJhbmdlKCBOVUxMLERlbnNpdHlfcGxvdF9vZl9jZW50cmFsaXRpZXMsIG5yb3cgPSAyLCAgaGVpZ2h0cz0gYygxLCAuNCksIGxhYmVscyA9IGMoJ2EnLCdiJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IC0wLjEsIHZqdXN0ID0gLjgpCgpEZW5zaXR5X3Bsb3Rfb2ZfY2VudHJhbGl0aWVzLnBhbmVsCgoKYGBgCgpgYGB7cn0KCmZiYSA8LSAgcmVhZC5jc3YoJy9ob21lL2FsZWphbmRyby9OQU1VX2luX3Byb2dyZXNzL0ZpZ3VyZXNfY3JlYXRpb24vQ29kZS9SZXN1bHRzX0FjZXZlZG9fZXRfYWxfMjAyMS8wMV9waHBwc19iaXBhcnRpdGVfZmx1eGVzX3NlbnNpc19vcHRpbWFsaXR5L0ZCQV9yZXN1bHRzLmNzdicpICU+JSAKICAgICAgICBzZWxlY3QoYygnSUQnLCAnUmVhY3Rpb24nLCAnRmx1eCcsICdTZW5zaXRpdml0eScpKSAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKCdJRCcpCgoKCmFwcGVuZF9mYmEgPC0gZnVuY3Rpb24oY2VudCl7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaG9sYSA8LSByb3duYW1lc190b19jb2x1bW4oZmJhKSAgJT4lIGlubmVyX2pvaW4ocm93bmFtZXNfdG9fY29sdW1uKGNlbnQpKSAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKCdyb3duYW1lJykKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4oaG9sYSkKfQoKCgptYXAobGlzdF9vZl9jZW50cywgYXBwZW5kX2ZiYSkgLT4gZmJhX2xpc3Rfb2ZfY2VudHMKCgpgYGAKCkhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShzY2FsZXMpCgoKYXNzaWduX25vZGVfdHlwZSA8LSBmdW5jdGlvbihkZil7CmRhdGEgPC0gZGYgICU+JSByb3duYW1lc190b19jb2x1bW4oJ0lEJykgJT4lIGZpbHRlcighc3RyX2RldGVjdChJRCwgcmVnZXgoJ0FBX3xBQTInLCBpZ25vcmVfY2FzZSA9IEYpKSkgICU+JQogICAgICAgICBtdXRhdGUoYE5vZGUgdHlwZWAgPSBpZmVsc2Uoc3RyX2RldGVjdChSZWFjdGlvbiwgcmVnZXgoJ1xcW1thLXpdQVxcXScsICAgICAgICAgICAgICBpZ25vcmVfY2FzZSA9IFQpKSwgJ0FzdHJvY3l0ZScsIE5BKSkgJT4lIAogICAgICAgICBtdXRhdGUoYE5vZGUgdHlwZWAgPSBpZmVsc2Uoc3RyX2RldGVjdChSZWFjdGlvbiwgcmVnZXgoJ1xcW1thLXpdTlxcXScsICAgICAgICAgICAgICBpZ25vcmVfY2FzZSA9IFQpKSwgJ05ldXJvbicsIGBOb2RlIHR5cGVgKSkgICAgJT4lIAogICAgICAgICBtdXRhdGUoYE5vZGUgdHlwZWAgPSBpZmVsc2UoIHN0cl9kZXRlY3QoUmVhY3Rpb24sIHJlZ2V4KCdcXFtbYS16XUFcXF18XFxbW2Etel1OXFxdJyxpZ25vcmVfY2FzZSA9IFQpLCBuZWdhdGUgPSBUKSwgJ0V4Y2hhbmdlJywgYE5vZGUgdHlwZWApKSAlPiUKICAgICAgICAgbXV0YXRlKGBOb2RlIHR5cGVgID0gaWZlbHNlKHN0cl9kZXRlY3QoUmVhY3Rpb24sIHJlZ2V4KCdcXFtbYS16XUFcXF0nLGlnbm9yZV9jYXNlID0gVCkpICYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cl9kZXRlY3QoTmFtZSwgcmVnZXgoJ0RNX3xEZW1hbmR8c2luaycsaWdub3JlX2Nhc2UgPSBUKSksICdTaW5rL0RlbWFuZCBBc3RybycsIGBOb2RlIHR5cGVgKSklPiUKICAgICAgICAgbXV0YXRlKGBOb2RlIHR5cGVgID0gaWZlbHNlKHN0cl9kZXRlY3QoUmVhY3Rpb24sIHJlZ2V4KCdcXFtbYS16XU5cXF0nLGlnbm9yZV9jYXNlID0gVCkpICYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cl9kZXRlY3QoTmFtZSwgcmVnZXgoJ0RNX3xEZW1hbmR8c2luaycsaWdub3JlX2Nhc2UgPSBUKSksICdTaW5rL0RlbWFuZCBOZXVyb24nLCBgTm9kZSB0eXBlYCkpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybihkYXRhKX0KCgoKbWFwKGZiYV9saXN0X29mX2NlbnRzLCBhc3NpZ25fbm9kZV90eXBlKQoKCgpgYGAKCgpgYGB7cn0KZGF0YSAlPiUgc2VsZWN0KG1hdGNoZXMoJ15cXGQrJykpICAgICAgIC0+IGNlbnRyYWxpdHkubWF0cml4CgpjZW50cmFsaXR5Lm1hdHJpeCAlPiUgYWJzICU+JSByb3dTdW1zKCkgIC0+IHRvdGFsLmFicy5jZW50cmFsaXR5ICN2ZWN0b3IgZGUgY2VudHJhbGlkYWRlcyB0b3RhbGVzCgpwc2V1ZG9Mb2cxMCA8LSBmdW5jdGlvbih4KSB7IGFzaW5oKHgvMikvbG9nKDEwKSB9CgpkYXRhJEZsdXhlcyAgICU+JSBhYnMgJT4lIGFzLm1hdHJpeCgpICAlPiUgcHNldWRvTG9nMTAgICU+JSByZXNjYWxlKGMoMCwxKSkgIC0+IGZsdXhlcwpkYXRhJFJlZENvc3RzICAlPiUgYWJzICU+JSBhcy5tYXRyaXgoKSAgICU+JSBwc2V1ZG9Mb2cxMCAgJT4lIHJlc2NhbGUoYygwLDEpKS0+IHNlbnNpdGl2aXRpZXMgCgpmbHV4ZXMgKyBzZW5zaXRpdml0aWVzICAtPiBzdW1tYXJ5emVkLm9wdGltYWxpdHkKYGBgCgpoZWF0bWFwCmBgYHtyIGZpZy5oZWlnaHQ9NS41LCBmaWcud2lkdGg9OS41LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKbGlicmFyeShDb21wbGV4SGVhdG1hcCkKc2NhbGVfcm93cyA8LSBmdW5jdGlvbih4KXt0KHNjYWxlKHQoeCkpKX0KCmRhdGEgJT4lIGNvbHVtbl90b19yb3duYW1lcygnSUQnKSAgICU+JSBzZWxlY3QobWF0Y2hlcygiXlxcZCsiKSkgJT4lIHQgJT4lIHNjYWxlX3Jvd3MgICU+JSBjb3IobWV0aG9kID0gImtlbmRhbGwiKSAtPiBteS5jb3JyLm1hdAoKbGVmdF9hbm5vdGF0aW9uIDwtIHJvd0Fubm90YXRpb24oYE5vZGVzYCA9IGRhdGEkYE5vZGUgdHlwZWAsIGNvbCA9IGxpc3QoIGBOb2Rlc2AgPSBjKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQXN0cm9jeXRlIiAgICAgICAgPSAiZGVlcHNreWJsdWUzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdFeGNoYW5nZScgICA9ICdkYXJrZ3JlZW4nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU2luay9EZW1hbmQgQXN0cm8nPSdkYXJrZ29sZGVucm9kJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5ldXJvbiIgICAgICAgICAgID0gImJyb3duMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTaW5rL0RlbWFuZCBOZXVyb24nPSAnYmx1ZXZpb2xldCcgKSkpCnBzZXVkb0xvZzEwIDwtIGZ1bmN0aW9uKHgpIHsgYXNpbmgoeC8yKS9sb2coMTApIH0KCnJpZ2h0X2Fubm90YXRpb24gPC0gICAKICByb3dBbm5vdGF0aW9uKGdhcCA9IHVuaXQoMTIsICJwb2ludHMiKSxDZSAgICAgID0gYW5ub19iYXJwbG90KGJhcl93aWR0aCA9IDAuMDEsd2lkdGggPSB1bml0KDEuNSwgImNtIiksIGJvcmRlciA9IFQsdG90YWwuYWJzLmNlbnRyYWxpdHksIGdwID0gZ3Bhcihjb2wgPSAnYXp1cmU0JykpLAogICAgICAgICAgICBPcCAgICAgICAgPSBhbm5vX2JhcnBsb3QoYmFyX3dpZHRoID0gMC4wMSx3aWR0aCA9IHVuaXQoMS41LCAiY20iKSAsYm9yZGVyID0gVCwgc3VtbWFyeXplZC5vcHRpbWFsaXR5LCBncCA9IGdwYXIoY29sID0gJ2F6dXJlNCcpKSkKCgpodCA8LSBIZWF0bWFwKG15LmNvcnIubWF0LCBuYW1lID0gIkNvcnJlbGF0aW9uIiwgIGxlZnRfYW5ub3RhdGlvbiA9bGVmdF9hbm5vdGF0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJpZ2h0X2Fubm90YXRpb249cmlnaHRfYW5ub3RhdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9jb2x1bW5zICA9IGZ1bmN0aW9uKG0pICAgZGlzdChtLCBtZXRob2QgPSAnZXVjbGlkZWFuJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29sdW1ucyAgICAgICAgICAgICAgPSBmdW5jdGlvbih4KSBmYXN0Y2x1c3Rlcjo6aGNsdXN0KGRpc3QoeCksICJhdmVyYWdlIiksCiAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzICAgPSBmdW5jdGlvbihtKSAgIGRpc3QobSwgbWV0aG9kID0gJ2V1Y2xpZGVhbicpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX3Jvd3MgICAgICAgICAgICAgICA9IGZ1bmN0aW9uKHgpIGZhc3RjbHVzdGVyOjpoY2x1c3QoZGlzdCh4KSwgImF2ZXJhZ2UiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3dfa20gPSAyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl9rbSA9IDIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyByb3dfc3BsaXQgPSBwYXN0ZTAoImNsdXN0ZXIgIiwgcGEkY2x1c3RlcmluZyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBjb2x1bW5fc3BsaXQgPSBwYXN0ZTAoImNsdXN0ZXIgIiwgcGEkY2x1c3RlcmluZyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJvcmRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvd19kZW5kX3dpZHRoICAgID0gdW5pdCgzLCAiY20iKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm93X2dhcCA9IHVuaXQoMiwgIm1tIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl9nYXAgPSB1bml0KDIsICJtbSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2lkdGggPSB1bml0KDEwLCAiY20iKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVpZ2h0ID0gdW5pdCgxMCwgImNtIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uX3RpdGxlID0gYygiQXN0cm9jeXRpYyBjbHVzdGVyIiwgIk5ldXJvbmFsIGNsdXN0ZXIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fdGl0bGVfZ3AgPSBncGFyKGZvbnRzaXplID0gMTApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm93X3RpdGxlX3JvdCAgPSAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2NvbHVtbl9uYW1lcyAgICA9IEYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19yb3dfbmFtZXMgICAgPSBGLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3dfbmFtZXNfZ3AgPSBncGFyKGZvbnRzaXplID0gOCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3dfdGl0bGUgPSBjKCJBc3Ryb2N5dGljXG4gY2x1c3RlciIsICJOZXVyb25hbFxuIGNsdXN0ZXIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm93X3RpdGxlX2dwID0gZ3Bhcihmb250c2l6ZSA9IDEwKSkKbXlfaGVhdG1hcCA9IGdyaWQuZ3JhYkV4cHIoZHJhdyhodCkpCgpodApgYGAKCgoKCmBgYHtyIGZpZy5oZWlnaHQ9NS41LCBmaWcud2lkdGg9MTR9CiB1cHBlcl9wYW5lbCA8LSBnZ2FycmFuZ2UoIERlbnNpdHlfcGxvdF9vZl9jZW50cmFsaXRpZXMucGFuZWwsICBteV9oZWF0bWFwLCBuY29sID0gMiwgd2lkdGhzID0gYyguNCwgMSksIGhlaWdodHMgPSBjKDEsLjMpLCBsYWJlbHMgPSBjKCcnLCdjJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IC00LCB2anVzdCA9IC44KQoKdXBwZXJfcGFuZWwKYGBgCgpEaXN0cmlidXRpb24gb2YgcGFpcndpc2UgY29ycmVsYXRpb25zCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnJvd19vcmRlcihodClbWzFdXSAtPiBhc3Ryb19jbHVzdGVyCnJvd19vcmRlcihodClbWzJdXSAtPiBuZXVyb25fY2x1c3RlcgpkYXRhJGBOb2RlIHR5cGVgICAgLT4gTm9kZXMKZGF0YSRJRFtuZXVyb25fY2x1c3Rlcl0gICAgIC0+IG5ldXJvbl9jbHVzdGVyX25hbWVzCmRhdGEkSURbYXN0cm9fY2x1c3Rlcl0gICAgICAtPiBhc3Ryb19jbHVzdGVyX25hbWVzCmh0QG1hdHJpeCAlPiUgYXMuZGF0YS5mcmFtZSAtPiBoZWF0bWFwX21hdHJpeAoKaGVhdG1hcF9tYXRyaXhbbmV1cm9uX2NsdXN0ZXJfbmFtZXMsbmV1cm9uX2NsdXN0ZXJfbmFtZXNdICU+JSBhcy5tYXRyaXggJT4lIGMgLT4gYE5ldXJvbmFsX2NsdXN0ZXJgCmhlYXRtYXBfbWF0cml4W2FzdHJvX2NsdXN0ZXJfbmFtZXMsYXN0cm9fY2x1c3Rlcl9uYW1lc10gJT4lIGFzLm1hdHJpeCAlPiUgYyAgLT5gQXN0cm9jeXRpY19jbHVzdGVyYCAKaGVhdG1hcF9tYXRyaXhbYXN0cm9fY2x1c3Rlcl9uYW1lcyxuZXVyb25fY2x1c3Rlcl9uYW1lc10gJT4lIGFzLm1hdHJpeCAlPiUgYyAgLT4gYE5ldXJvbl92c19Bc3Ryb2N5dGVgCgpkYXRhLmZyYW1lKGBOZXVyb25hbF9jbHVzdGVyYCkgJT4lIGdhdGhlciAgLT4gQQpkYXRhLmZyYW1lKGBBc3Ryb2N5dGljX2NsdXN0ZXJgKSAlPiUgZ2F0aGVyICAgLT4gQgpkYXRhLmZyYW1lKCBgTmV1cm9uX3ZzX0FzdHJvY3l0ZWApICU+JSBnYXRoZXIgLT4gQwpxdWFkcyA8LSByYmluZChBLEIsQykKY29sbmFtZXMocXVhZHMpIDwtIGMoIkNvbXBhcmlzb24iLCJOb2RlIGNvcnJlbGF0aW9uIikKCiNxdWFkcyRDb21wYXJpc29uICU+JSB1bmlxdWUoKQoKbXlfY29tcGFyaXNvbnMgPC0gbGlzdCggYygiTmV1cm9uYWxfY2x1c3RlciIsICJBc3Ryb2N5dGljX2NsdXN0ZXIiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgIGMoIk5ldXJvbmFsX2NsdXN0ZXIiLCAiTmV1cm9uX3ZzX0FzdHJvY3l0ZSIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgYygiQXN0cm9jeXRpY19jbHVzdGVyIiwgIk5ldXJvbl92c19Bc3Ryb2N5dGUiKSApCmBgYApgYGB7ciBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0zfQpnZ3Bsb3QocXVhZHMsIGFlcyh4PUNvbXBhcmlzb24sIHk9YE5vZGUgY29ycmVsYXRpb25gLCBmaWxsPUNvbXBhcmlzb24pKSArIAogICAgZ2VvbV92aW9saW4odHJpbT1GLCBjb2xvdXIgPSAiYXp1cmU0IikrICAKICAgIHN0YXRfY29tcGFyZV9tZWFucyggdmp1c3Q9IC0xLiwgaGp1c3Q9IDAsIGNvbXBhcmlzb25zID0gbXlfY29tcGFyaXNvbnMsIG1ldGhvZCA9ICJ3aWxjb3gudGVzdCIsIHAuYWRqdXN0Lm1ldGhvZCA9ICJib25mZXJyb25pIiwgbGFiZWwgPSAicC5zaWduaWYiKSsKICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYWxwaGEoYygiZGVlcHNreWJsdWUzIiwgImJsdWV2aW9sZXQiLCAiYnJvd24yIiksIC44KSkgKyAKICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksbGVnZW5kLnBvc2l0aW9uPSJub25lIiwgIGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNTAsIGhqdXN0ID0gMSkpICArIAogICNnZ3RpdGxlKCJOZXVyb25hbCBzZWxmLWNvcnJlbGF0aW9ucyIpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cz1jKC0wLjcsIDIpKSArIGdlb21faGxpbmUoeWludGVyY2VwdD0wLCBsaW5ldHlwZT0iZGFzaGVkIiwgY29sb3IgPSAiZGFya3JlZCIsICBzaXplPTEpLT4gY29ycl9jb21wYXJpc29ucwoKY29ycl9jb21wYXJpc29ucwpgYGAKCgpgYGB7cn0KbGlicmFyeShQcm9qZWN0aW9uQmFzZWRDbHVzdGVyaW5nKQpsaWJyYXJ5KFBDQXRvb2xzKQpsaWJyYXJ5KG1hZ3JpdHRyKQoKcCA8LSBwY2EobXkuY29yci5tYXQpCgpjYmluZChwJHJvdGF0ZWQkUEMxLCBwJHJvdGF0ZWQkUEMyKSAlPiUgYXMubWF0cml4ICU+JSBhcy5kYXRhLmZyYW1lLT4gbXlfcGNhCgoKCgpteV9wY2EgJT4lICBjYmluZCggZGF0YSRgTm9kZSB0eXBlYCkgJT4lIHNldF9jb2xuYW1lcyhjKCdQQzEnLCdQQzInLCdub2RlJykpIC0+IHRvLnBjYS5zY2F0dGVyCgpzdW1tYXJ5emVkLm9wdGltYWxpdHkgLT4gT3AKdG90YWwuYWJzLmNlbnRyYWxpdHkgLT4gQ2VudHJhbGl0eQpDZW50cmFsaXR5IC0+IENlCgpiIDwtIGdncGxvdCh0by5wY2Euc2NhdHRlciwgYWVzKHggPSBQQzEsIHkgPSBQQzIpKSAKYiArICAgc2NhbGVfY29sb3JfbWFudWFsKGxhYmVscyA9IGMoIkFzdHJvIiwgIkV4Y2giLCdOZXUnLCdTaS9EZSBBc3QnLCAnU2kvRGUgTmV1JyksIHZhbHVlcyA9ICBjKCJkZWVwc2t5Ymx1ZTMiLCdkYXJrZ3JlZW4nLCdicm93bjInLCdkYXJrZ29sZGVucm9kJywnYmx1ZXZpb2xldCcpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0icmlnaHQiLCBsZWdlbmQuYm94ID0gInZlcnRpY2FsIikrIGdlb21fcG9pbnQoYWVzKHNpemUgPSBPcCwgY29sb3IgPSBub2RlKSktPiBwY2Ffbm9kZV9SZWRDb3N0cwoKCgoKYiA8LSBnZ3Bsb3QodG8ucGNhLnNjYXR0ZXIsIGFlcyh4ID0gUEMxLCB5ID0gUEMyKSkgCmIgKyAgIHNjYWxlX2NvbG9yX21hbnVhbChsYWJlbHMgPSBjKCJBc3RybyIsICJFeGNoIiwnTmV1JywnU2kvRGUgQXN0JywgJ1NpL0RlIE5ldScpLCB2YWx1ZXMgPSAgYygiZGVlcHNreWJsdWUzIiwnZGFya2dyZWVuJywnYnJvd24yJywnZGFya2dvbGRlbnJvZCcsJ2JsdWV2aW9sZXQnKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0icmlnaHQiLCBsZWdlbmQuYm94ID0gInZlcnRpY2FsIikrIGdlb21fcG9pbnQoYWVzKCBzaXplID0gIENlLGNvbG9yID0gbm9kZSkpLT4gcGNhX25vZGVfY2VudHJhbGl0eQpgYGAKCmBgYHtyfQoKCmJvdHRvbV9wYW5lbCA8LSBnZ2FycmFuZ2UoIGNvcnJfY29tcGFyaXNvbnMscGNhX25vZGVfY2VudHJhbGl0eSwgcGNhX25vZGVfUmVkQ29zdHMgICwgbmNvbCA9IDMsIHdpZHRocyAgPSBjKC40LDEsMSkgLCBsYWJlbHMgPSBjKCdkJywnZScsJ2YnKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0ID0gMCwgdmp1c3QgPSAxKQoKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTksIGZpZy53aWR0aD0xM30KCgpwYW5lbF93aXRob3V0X2dyYXBoIDwtICBnZ2FycmFuZ2UodXBwZXJfcGFuZWwsIGJvdHRvbV9wYW5lbCwgbnJvdyA9IDIsIGhlaWdodHMgPSBjKDEsMC41KSkKcGFuZWxfd2l0aG91dF9ncmFwaAoKYGBgCgpgYGB7cn0KZ2dzYXZlKGZpbGU9InBhbmVsX3dpdGhvdXRfZ3JhcGgucG5nIiwgcGxvdD1wYW5lbF93aXRob3V0X2dyYXBoLCB3aWR0aD0xMywgaGVpZ2h0PTksIGRwaSA9IDMyMCkKYGBgCgoKTm9kZSB0eXBlcyBmb3IgTmV0d29ya3ggKHB5dGhvbikgYXR0cmlidXRlcwpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShtYWdyaXR0cikKClJlc3VsdHMgPC0gcmVhZF9jc3YoIlJlc3VsdHMuY3N2IikKCmRhdGFfTmV0d29ya3ggPC0gUmVzdWx0cyU+JSAKICAgICAgICAgbXV0YXRlKGBOb2RlX3R5cGVgID0gaWZlbHNlKHN0cl9kZXRlY3QoRm9ybXVsYSwgcmVnZXgoJ1xcW1thLXpdQVxcXScsICAgICAgICAgICAgICBpZ25vcmVfY2FzZSA9IFQpKSwgJ0FzdHJvY3l0ZScsIE5BKSkgJT4lIAogICAgICAgICBtdXRhdGUoYE5vZGVfdHlwZWAgPSBpZmVsc2Uoc3RyX2RldGVjdChGb3JtdWxhLCByZWdleCgnXFxbW2Etel1OXFxdJywgICAgICAgICAgICAgIGlnbm9yZV9jYXNlID0gVCkpLCAnTmV1cm9uJywgYE5vZGVfdHlwZWApKSAgICAlPiUgCiAgICAgICAgIG11dGF0ZShgTm9kZV90eXBlYCA9IGlmZWxzZSggc3RyX2RldGVjdChGb3JtdWxhLCByZWdleCgnXFxbW2Etel1BXFxdfFxcW1thLXpdTlxcXScsaWdub3JlX2Nhc2UgPSBUKSwgbmVnYXRlID0gVCksICdFeGNoYW5nZScsIGBOb2RlX3R5cGVgKSkgJT4lCiAgICAgICAgIG11dGF0ZShgTm9kZV90eXBlYCA9IGlmZWxzZShzdHJfZGV0ZWN0KEZvcm11bGEsIHJlZ2V4KCdcXFtbYS16XUFcXF0nLGlnbm9yZV9jYXNlID0gVCkpICYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cl9kZXRlY3QoTmFtZSwgcmVnZXgoJ0RNX3xEZW1hbmR8c2luaycsaWdub3JlX2Nhc2UgPSBUKSksICdTaW5rL0RlbWFuZCBBc3RybycsIGBOb2RlX3R5cGVgKSklPiUKICAgICAgICAgbXV0YXRlKGBOb2RlX3R5cGVgID0gaWZlbHNlKHN0cl9kZXRlY3QoRm9ybXVsYSwgcmVnZXgoJ1xcW1thLXpdTlxcXScsaWdub3JlX2Nhc2UgPSBUKSkgJiAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyX2RldGVjdChOYW1lLCByZWdleCgnRE1ffERlbWFuZHxzaW5rJyxpZ25vcmVfY2FzZSA9IFQpKSwgJ1NpbmsvRGVtYW5kIE5ldXJvbicsIGBOb2RlX3R5cGVgKSkKCgpkYXRhX05ldHdvcmt4ICU8PiUgc2VsZWN0KGMoSUQsIE5vZGVfdHlwZSkpCgp3cml0ZV9jc3YoZGF0YV9OZXR3b3JreCwgJ2RhdGFfTmV0d29ya3guY3N2JykKCmBgYAoKCgoKCgoK